#!/usr/bin/python

#Audio Tools, a module and set of tools for manipulating audio data
#Copyright (C) 2007  Brian Langenberger

#This program is free software; you can redistribute it and/or modify
#it under the terms of the GNU General Public License as published by
#the Free Software Foundation; either version 2 of the License, or
#(at your option) any later version.

#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#GNU General Public License for more details.

#You should have received a copy of the GNU General Public License
#along with this program; if not, write to the Free Software
#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA


import audiotools
import audiotools.cue
import optparse,sys,os,os.path

MAX_CPUS = audiotools.MAX_JOBS

def extract_part(input_filename, input_pcm, output_filename, quality, metadata):
    print (u"%s -> %s" % \
               (input_filename.decode(audiotools.FS_ENCODING),
                output_filename.decode(audiotools.FS_ENCODING))).encode(
        audiotools.IO_ENCODING)

    destination_audiofile = AudioType.from_pcm(
        output_filename,
        input_pcm,
        quality)

    encoded_filenames.append(filename)

    if (metadata is not None):
        destination_audiofile.set_metadata(metadata)

def has_embedded_cuesheet(audiofile):
    #FlacAudio is typically the only class to contain a
    #sub_pcm_tracks() method and
    #FlacMetaData with a cuesheet attribute signfifies
    #the FLAC actually contains a cuesheet with the necessary sub-tracks
    return (hasattr(audiofile,"sub_pcm_tracks") and
            hasattr(audiofile.get_metadata(),"cuesheet"))


if (__name__ == '__main__'):
    parser = optparse.OptionParser(
        '%prog [--cue cuesheet] [-t track type ] [-x XMCD file] ' + \
        '[-d directory] [-q quality] ' + \
        '<track>',
        version="Python Audio Tools %s" % (audiotools.VERSION))

    parser.add_option('--cue',
                      action='store',
                      type='string',
                      dest='cuesheet',
                      help='the cuesheet to use for splitting track')

    parser.add_option('-x','--xmcd',
                      action='store',
                      type='string',
                      dest='xmcd',
                      help='an XMCD file to get audio metadata from')

    parser.add_option('-d','--dir',
                      action='store',
                      type='string',
                      dest='dir',
                      default='.',
                      help='the directory to store extracted audio files')

    parser.add_option('-q','--quality',
                      action='store',
                      type='string',
                      dest='quality',
                      help='the quality to store audio values at')

    parser.add_option('-t','--type',
                      action='store',
                      dest='type',
                      choices=audiotools.TYPE_MAP.keys(),
                      default='wav',
                      help='the type of audio value to convert to')

    parser.add_option('-j','--joint',
                      action='store',
                      type='int',
                      default=MAX_CPUS,
                      dest='max_processes',
                      help='the maximum number of processes to run at a time')

    parser.add_option('--no-replay-gain',
                      action='store_false',
                      dest='add_replay_gain',
                      default=True,
                      help='do not add ReplayGain metadata to newly extracted tracks')

    (options,args) = parser.parse_args()

    #get the AudioFile class we are converted to
    AudioType = audiotools.TYPE_MAP[options.type]

    #ensure the selected compression is compatible with that class
    if (options.quality == 'help'):
        if (len(AudioType.COMPRESSION_MODES) > 1):
            print >>sys.stderr,"*** Available compression types for %s:" % \
                  (AudioType.SUFFIX)
            for mode in AudioType.COMPRESSION_MODES:
                print >>sys.stderr,mode
        else:
            print >>sys.stderr,"*** Audio type %s has no compression modes" % \
                  (AudioType.SUFFIX)
        sys.exit(0)
    elif (options.quality == None):
        options.quality = AudioType.DEFAULT_COMPRESSION
    elif (options.quality not in AudioType.COMPRESSION_MODES):
        print >>sys.stderr,"*** \"%s\" is not a supported compression " % \
              (options.quality) + "mode for type \"%s\"" % \
              (AudioType.SUFFIX)
        sys.exit(1)

    if (len(args) != 1):
        print >>sys.stderr,"*** You must specify exactly 1 supported audio file"
        sys.exit(1)

    try:
        audiofile = audiotools.open(args[0])
    except audiotools.UnsupportedFile:
        print >>sys.stderr,"*** You must specify exactly 1 supported audio file"
        sys.exit(1)

    if ((options.cuesheet is None) and (not has_embedded_cuesheet(audiofile))):
        print >>sys.stderr,"*** You must specify a cuesheet to split audio file"
        sys.exit(1)

    #if we're using an XMCD file, use that file for MetaData
    if (options.xmcd != None):
        try:
            xmcd = audiotools.parse_xmcd_file(options.xmcd)
        except audiotools.XMCDException:
            print >>sys.stderr,"*** Invalid XMCD file"
            sys.exit(1)
    else:
        #if we're not using an XMCD file, no MetaData
        #(I could theoretically pull some from the base track
        # but there usually isn't any and it's not very practical)
        xmcd = None

    base_directory = options.dir
    encoded_filenames = []
    queue = audiotools.ExecQueue()

    if (options.cuesheet is not None):
        #grab the cuesheet we're using to split tracks
        #(this overrides an embedded cuesheet)
        try:
            cuesheet = audiotools.cue.read_cuesheet(options.cuesheet)
        except audiotools.cue.CueException,msg:
            print >>sys.stderr,"*** Cuesheet error: %s" % (msg)
            sys.exit(1)

        if (not cuesheet.single_file_type()):
            print >>sys.stderr,"*** Cuesheet error: %s" % \
                "cuesheet is not formatted for splitting a single track"
            sys.exit(1)

        if (list(cuesheet.pcm_lengths(audiofile.total_frames()))[-1] <= 0):
            print >>sys.stderr,"*** Cuesheet error: %s" % \
                "cuesheet too short for track being split"
            sys.exit(1)

        split_pcm = audiotools.pcm_split(audiofile.to_pcm(),
                                         cuesheet.pcm_lengths(audiofile.total_frames()))
    else:
        split_pcm = list(audiofile.sub_pcm_tracks())
        #this hackery is terrible form,
        #but the only way we'll be able to wait() correctly
        for p in split_pcm:
            p.process = None


    for (i,pcmreader) in enumerate(split_pcm):
        track_number = i + 1

        if (xmcd is not None):
            filename = os.path.join(
                base_directory,
                AudioType.track_name(track_number,
                                     xmcd[track_number]))
            metadata = xmcd[track_number]
        else:
            filename = os.path.join(
                base_directory,
                AudioType.track_name(track_number,
                                     None))

            metadata = None

        audiotools.make_dirs(filename)
        encoded_filenames.append(filename)

        queue.execute(extract_part,
                      (audiofile.filename,
                       pcmreader,
                       filename,
                       options.quality,
                       metadata))

    queue.run(options.max_processes)

    if (options.add_replay_gain and AudioType.can_add_replay_gain()):
        print >>sys.stderr,"* Adding ReplayGain metadata.  This may take some time."
        AudioType.add_replay_gain(encoded_filenames)
